package com.gw.vip.server.service;

import com.gw.vip.common.constants.VipConstants;
import com.gw.vip.common.enums.StatusEnums;
import com.gw.vip.common.exception.ExceptionEnums;
import com.gw.vip.common.exception.ServiceException;
import com.gw.vip.common.util.Snowflake;
import com.gw.vip.server.bo.ChargeRsp;
import com.gw.vip.server.bo.RefundRsp;
import com.gw.vip.server.bo.SaleRsp;
import com.gw.vip.server.enums.TxnTypeEnums;
import com.gw.vip.server.enums.AccTypeEnums;
import com.gw.vip.server.po.SysCapitalType;
import com.gw.vip.server.po.Trans;
import com.gw.vip.server.po.Account;
import com.gw.vip.server.po.UserInfo;
import com.gw.vip.server.service.dao_service.SysCapitalTypeService;
import com.gw.vip.server.service.dao_service.TransAllService;
import com.gw.vip.server.service.dao_service.TransService;
import com.gw.vip.server.service.dao_service.AccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author GW
 * @description
 * @since 2022-05-04
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class AccountBiz {

    private final AccountService accountService;
    private final Snowflake snowflake;
    private final SysCapitalTypeService sysCapitalTypeService;
    private final TransService transService;
    private final TransAllService transAllService;

    /**
     * 充值
     * @param userInfo
     * @param txnAmt
     * @param capitalId
     */
    public ChargeRsp charge(UserInfo userInfo, String transId, BigDecimal txnAmt, String capitalId, LocalDateTime txnDateTime) {
        Account account = accountService.getMainAcc(userInfo.getId(), capitalId);
        //开通帐户
        if (account ==null){
            account = this.openAccount(userInfo.getId(), capitalId, txnDateTime);
            account.setAccType(AccTypeEnums.MAIN.getKey());
        }

        Trans trans = accountService.change(transId, null, TxnTypeEnums.CHARGE.getKey(), txnAmt, account.getUserId(), account.getId(), capitalId, txnDateTime);

        ChargeRsp chargeRsp = new ChargeRsp();
        chargeRsp.setTrans(trans);
        return chargeRsp;
    }

    /**
     * 消费
     * @param userId
     * @param transId
     * @param txnAmt
     * @param txnDateTime
     * @return
     */
    public SaleRsp sale(String userId, String transId, BigDecimal txnAmt, LocalDateTime txnDateTime) {
        //剩余扣款金额
        BigDecimal nextTxnAmt = txnAmt;
        List<Trans> transList = new ArrayList<>();
        List<Account> accountList = accountService.listByUserId(userId);
        List<SysCapitalType> sysCapitalTypeList = sysCapitalTypeService.list();
        accountList.sort((userAccount1, userAccount2) -> sysCapitalTypeService.getPayOrder(sysCapitalTypeList, userAccount2.getCapitalId()).compareTo(sysCapitalTypeService.getPayOrder(sysCapitalTypeList, userAccount1.getCapitalId())));
        for (Account account : accountList) {
            //此帐户最大可扣金额
            BigDecimal maxAmt = account.getAmt().subtract(account.getLockAmt());
            Trans accountTrans;
            if (maxAmt.compareTo(nextTxnAmt) >= 0) {
                //可用余额够扣
                accountTrans = accountService.change(transId, null, TxnTypeEnums.SALE.getKey(), nextTxnAmt, userId, account.getId(), account.getCapitalId(), txnDateTime);
                nextTxnAmt = BigDecimal.ZERO;
            } else {
                //可用余额不够扣
                accountTrans = accountService.change(transId, null, TxnTypeEnums.SALE.getKey(), maxAmt, userId, account.getId(), account.getCapitalId(), txnDateTime);
                nextTxnAmt = nextTxnAmt.subtract(maxAmt);
            }
            transList.add(accountTrans);
            if (nextTxnAmt.compareTo(BigDecimal.ZERO) == 0) {
                //扣款完成
                break;
            }
        }
        if (nextTxnAmt.compareTo(BigDecimal.ZERO) > 0) {
            //总可用余额不够扣
            log.warn("用户【{}】总可用余额不够扣, 交易金额【{}】, 还需扣款【{}】", userId, txnAmt, nextTxnAmt);
            throw new ServiceException(ExceptionEnums.LOW_AMT);
        }

        SaleRsp saleRsp = new SaleRsp();
        saleRsp.setTransList(transList);
        return saleRsp;
    }

    /**
     * 退款
     * @param userId
     * @param transId
     * @param txnAmt
     * @param txnDateTime
     * @param srcTransId
     * @return
     */
    public RefundRsp refund(String userId, String transId, BigDecimal txnAmt, LocalDateTime txnDateTime, String srcTransId) {
        //找到原交易明细
        List<Trans> srcAccountTransList = transService.listByOrderId(srcTransId);
        //金额检查
        BigDecimal srcTxnAmt = srcAccountTransList.stream().map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (txnAmt.compareTo(srcTxnAmt) != 0) {
            log.error("原交易金额和退款金额不一致。原交易总额【{}】, 退款金额【{}】", srcTxnAmt, txnAmt);
            throw new ServiceException("金额不一致！");
        }
        //可退款金额检查
        for (Trans srcAccountTrans:srcAccountTransList){
            if (srcAccountTrans.getRefundAmt().compareTo(BigDecimal.ZERO)>0){
                log.error("订单明细【{}】已经存在退款！", srcAccountTrans.getRefundAmt());
                throw new ServiceException("订单已经存在退款！");
            }
        }

        List<Trans> transList = new ArrayList<>();
        //退款
        for (Trans srcTrans :srcAccountTransList){
            Account account = accountService.getById(srcTrans.getAccount());
            if (account == null){
                log.error("帐户【{}】不存在！", srcTrans.getAccount());
                throw new ServiceException("帐户不存在！");
            }
            Trans accountTrans = accountService.change(transId, srcTrans.getId(), TxnTypeEnums.REFUND.getKey(), srcTrans.getTxnAmt(), userId, srcTrans.getAccount(), srcTrans.getCapitalId(), txnDateTime);
            transList.add(accountTrans);
            //更新原交易明细
            srcTrans.setRefundAmt(srcTrans.getTxnAmt());
            if (!transAllService.updateById(srcTrans)){
                log.error("更新原支付订单明细失败");
                throw new ServiceException("更新原支付订单明细失败");
            }
        }

        RefundRsp refundRsp = new RefundRsp();
        refundRsp.setTransList(transList);
        return refundRsp;
    }

    //-------------------------------- 辅助函数 -------------------------------------//

    /**
     * 开通帐户
     * @param userId
     * @param capitalId
     * @param transDateTime
     * @return
     */
    private Account openAccount(String userId, String capitalId, LocalDateTime transDateTime) {
        Account account = new Account();
        account.setId(snowflake.nextStringId());
        account.setUserId(userId);
        account.setCapitalId(capitalId);
        account.setAmt(BigDecimal.ZERO);
        account.setLockAmt(BigDecimal.ZERO);
        account.setLastAmt(BigDecimal.ZERO);
        account.setLastLockAmt(BigDecimal.ZERO);
        account.setClearAmt(BigDecimal.ZERO);
        account.setStatus(StatusEnums.SUCCESS.getCode());
        account.setCrtTime(transDateTime);
        account.setCrtName(VipConstants.SYS);
        account.setUpdTime(account.getCrtTime());
        account.setUpdName(VipConstants.SYS);
        if (!accountService.save(account)){
            log.error("保存帐户失败！用户【{}】", userId);
            throw new ServiceException("保存帐户失败！");
        }
        return account;
    }
}
